package io.renren.utils;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.ibatis.cache.Cache;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisConnectionException;

import java.io.*;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 
/*
 * 使用第三方缓存服务器，处理二级缓存
 * <a href="http://www.zyiqibook.com">在一起 学习交流分享网 功能源码分享</a>
 * @author xfxpeter@gmail.com
 */
@Component
public class MybatisRedisCache implements Cache {
     
    private static final Logger logger = Logger.getLogger(MybatisRedisCache.class);
     
    /** The ReadWriteLock. */
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private JedisPool jedisPool;
    private static final int DB_INDEX = 1;
    private static final String COMMON_CACHE_KEY = "OBJECT:";
    private static final String UTF_8 = "utf-8";
    private String id;
    
    @Value("${redis.host}")
    private String host;
    @Value("${redis.port}")
    private int port;
    @Value("${redis.timeout}")
    private int timeout;
    @Value("${redis.pwd}")
    private String pwd;
    @Value("${redis.maxIdle}")
    private int maxIdle;
    
    public MybatisRedisCache() {
    	
    }
    private void initJedisPool() {
    	if(jedisPool==null) {
    		JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxIdle(maxIdle);
            jedisPool = new JedisPool(config, host,
                    port,
                    timeout,
                    pwd);
    	}
    	
    }
    public MybatisRedisCache(final String id) {
    	this();
        if (id == null) {
            throw new IllegalArgumentException("必须传入ID");
        }
        logger.debug("MybatisRedisCache:id=" + id);
        this.id = id;
    }
    public JedisPool getJedisPool(){
    	return jedisPool;
    }

    public boolean isRedisConnect(){
        try{
        	initJedisPool();
            jedisPool.getResource();
        }catch(JedisConnectionException e){
            return false;
        }catch (Exception e){
            return false;
        }
        return  true;
    }


    /**
     * Hget all map.
     *
     * @param key     the key
     * @param dbIndex the db index
     * @return the map
     * @author liujp
     * @date 2017 -07-25 03:34:55 下午
     */
    public Map<String, String> hgetAll(String key, int dbIndex){
        Jedis jedis = null;
        boolean borrowOrOprSuccess = true;
        try {
        	initJedisPool();
            jedis = jedisPool.getResource();
            jedis.select(dbIndex);
            return jedis.hgetAll(key);
        } catch (Exception e) {
            borrowOrOprSuccess = false;
            if (jedis != null)
                jedisPool.returnBrokenResource(jedis);
        } finally {
            if (borrowOrOprSuccess)
                jedisPool.returnResource(jedis);
        }
        return null;
    }
    /**
     * 返回hashes 中对应key 的field
     * @param key
     * @param field
     * @param dbIndex
     */
    public String hget(String key,String field,int dbIndex){
    	Jedis jedis = null;
        boolean borrowOrOprSuccess = true;
        try {
        	initJedisPool();
            jedis = jedisPool.getResource();
            jedis.select(dbIndex);
            return jedis.hget(key, field);
        } catch (Exception e) {
            borrowOrOprSuccess = false;
            if (jedis != null)
                jedisPool.returnBrokenResource(jedis);
        } finally {
            if (borrowOrOprSuccess)
                jedisPool.returnResource(jedis);
        }
        return null;
    }

    /**
     * 添加 对应key 的field
     * @param key
     * @param field
     * @param dbIndex
     */
    public void hset(String key,String field,Object value,int dbIndex){
        Jedis jedis = null;
        boolean borrowOrOprSuccess = true;
        try {
        	initJedisPool();
            jedis = jedisPool.getResource();
            jedis.select(dbIndex);
            jedis.hset(key, field,value.toString());
        } catch (Exception e) {
            borrowOrOprSuccess = false;
            if (jedis != null)
                jedisPool.returnBrokenResource(jedis);
        } finally {
            if (borrowOrOprSuccess)
                jedisPool.returnResource(jedis);
        }
    }
    /**
     * 按照一定规则标识key
     */
    private String getKey(Object key) {
    	if(key instanceof String){
    		return (String)key;
    	}
        StringBuilder accum = new StringBuilder();
        accum.append(COMMON_CACHE_KEY);
        accum.append(DigestUtils.md5Hex(String.valueOf(key)));
        return accum.toString();
    }
  
    /**
     * redis key规则前缀
     */
    private String getKeys() {
        return COMMON_CACHE_KEY + this.id + ":*";
    }
 
    
     
    
 
    
 
    @Override
    public String getId() {
        return this.id;
    }
    
    @Override
    public int getSize() {
        Jedis jedis = null;
        int result = 0;
        boolean borrowOrOprSuccess = true;
        try {
        	initJedisPool();
            jedis = jedisPool.getResource();
            jedis.select(DB_INDEX);
            Set<byte[]> keys = jedis.keys(getKeys().getBytes(UTF_8));
            if (null != keys && !keys.isEmpty()) {
                result = keys.size();
            }
            logger.debug(this.id+"---->>>>总缓存数:" + result);
        } catch (Exception e) {
            borrowOrOprSuccess = false;
            if (jedis != null)
                jedisPool.returnBrokenResource(jedis);
        } finally {
            if (borrowOrOprSuccess)
                jedisPool.returnResource(jedis);
        }
        return result;
 
    }
 
    @Override
    public void putObject(Object key, Object value) {
        Jedis jedis = null;
        boolean borrowOrOprSuccess = true;
        try {
        	initJedisPool();
            jedis = jedisPool.getResource();
            jedis.select(DB_INDEX);
             
            byte[] keys = getKey(key).getBytes(UTF_8);
            jedis.set(keys, SerializeUtil.serialize(value));
            logger.debug("添加缓存--------"+this.id);
        } catch (Exception e) {
            borrowOrOprSuccess = false;
            if (jedis != null)
                jedisPool.returnBrokenResource(jedis);
        } finally {
            if (borrowOrOprSuccess)
                jedisPool.returnResource(jedis);
        }
 
    }
    public void putString(Object key, String value) {
        Jedis jedis = null;
        boolean borrowOrOprSuccess = true;
        try {
        	initJedisPool();
            jedis = jedisPool.getResource();
            jedis.select(DB_INDEX);
             
            byte[] keys = getKey(key).getBytes(UTF_8);
            jedis.set(keys, value.getBytes(UTF_8));
            jedis.persist(keys);
            
            logger.debug("添加缓存--------"+this.id);
        } catch (Exception e) {
            borrowOrOprSuccess = false;
            if (jedis != null)
                jedisPool.returnBrokenResource(jedis);
        } finally {
            if (borrowOrOprSuccess)
                jedisPool.returnResource(jedis);
        }
 
    }
    public String getString(Object key) {
        Jedis jedis = null;
        byte [] value = null;
        boolean borrowOrOprSuccess = true;
        try {
        	initJedisPool();
            jedis = jedisPool.getResource();
            jedis.select(DB_INDEX);
            value = jedis.get(getKey(key).getBytes(UTF_8));
            logger.debug("从缓存中获取-----"+this.id);
        } catch (Exception e) {
            borrowOrOprSuccess = false;
            if (jedis != null)
                jedisPool.returnBrokenResource(jedis);
        } finally {
            if (borrowOrOprSuccess)
                jedisPool.returnResource(jedis);
        }
        return value==null?null:new String(value, Charset.forName(UTF_8));
    }
    @Override
    public Object getObject(Object key) {
        Jedis jedis = null;
        Object value = null;
        boolean borrowOrOprSuccess = true;
        try {
        	initJedisPool();
            jedis = jedisPool.getResource();
            jedis.select(DB_INDEX);
            value = SerializeUtil.unserialize(jedis.get(getKey(key).getBytes(UTF_8)));
            logger.debug("从缓存中获取-----"+this.id);
        } catch (Exception e) {
            borrowOrOprSuccess = false;
            if (jedis != null)
                jedisPool.returnBrokenResource(jedis);
        } finally {
            if (borrowOrOprSuccess)
                jedisPool.returnResource(jedis);
        }
        return value;
    }
 
    @Override
    public Object removeObject(Object key) {
    	
        return this.removeObject(key, DB_INDEX);
    }
    
    public Object removeObject(Object key, int dbIndex) {
    	Jedis jedis = null;
    	Object value = null;
    	boolean borrowOrOprSuccess = true;
    	try {
    		initJedisPool();
    		jedis = jedisPool.getResource();
    		jedis.select(dbIndex);
    		value = jedis.del(getKey(key).getBytes(UTF_8));
    		logger.debug("LRU算法从缓存中移除-----"+this.id);
    	} catch (Exception e) {
    		borrowOrOprSuccess = false;
    		if (jedis != null)
    			jedisPool.returnBrokenResource(jedis);
    	} finally {
    		if (borrowOrOprSuccess)
    			jedisPool.returnResource(jedis);
    	}
    	return value;
    }
 
    @Override
    public void clear() {
        Jedis jedis = null;
        boolean borrowOrOprSuccess = true;
        try {
        	initJedisPool();
            jedis = jedisPool.getResource();
            jedis.select(DB_INDEX);
            Set<byte[]> keys = jedis.keys(getKeys().getBytes(UTF_8));
            logger.debug("出现CUD操作，清空对应Mapper缓存======>"+keys.size());
            for (byte[] key : keys) {
                jedis.del(key);
            }
        } catch (Exception e) {
            borrowOrOprSuccess = false;
            if (jedis != null)
                jedisPool.returnBrokenResource(jedis);
        } finally {
            if (borrowOrOprSuccess)
                jedisPool.returnResource(jedis);
        }
    }
 
    @Override
    public ReadWriteLock getReadWriteLock() {
        return readWriteLock;
    }

}